dict methods

dir({})

Output:

['__class__',
 '__contains__',
 '__delattr__',
 '__delitem__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__setitem__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'clear',
 'copy',
 'fromkeys',
 'get',
 'items',
 'keys',
 'pop',
 'popitem',
 'setdefault',
 'update',
 'values']

Methods without double undescores:

[attr for attr in dir({}) if '__' not in attr]

Output:

['clear',
 'copy',
 'fromkeys',
 'get',
 'items',
 'keys',
 'pop',
 'popitem',
 'setdefault',
 'update',
 'values']

dict.clear()

D = {'one': 'uno'}
D['two'] = 'dos'       # dict mutated: {'one': 'uno', 'two': 'dos'}
D.clear()              # {} - remove all entries in a dict without destroying dict

D = {1: 1}
id(D)      # 90038136
D.clear()
id(D)      # 90038136

D = {'one': 'uno'}
D1 = D      # create reference to a dict D
D, D1      # ({'one': 'uno'}, {'one': 'uno'})

D = {}      # D pointed to empty dict (not reassigned to new dict)
D, D1      # ({}, {'one': 'uno'}) - D1 reference unchanged

D = D1      # ({'one': 'uno'}, {'one': 'uno'}) - recover entries in D

D.clear()   # in case of clear()
D, D1      # ({}, {}) - both are cleared

dict.copy()

Shallow copy - only the keys will be copied.
The values will be the references to the values of the original dictionary.
Copy does not make a recursive copies of all dict substructures.
For deep copy we have to use “copy” python module
D = {'one': 'uno'}
D1 = D.copy()
D.clear()   # changes in D will net be reflected to D1 if the changes are on level of the keys
D, D1      # ({}, {'one': 'uno'}) - D1 was not changed

# BUT in case of nested dictionary
D = {'one': {'spanish': 'uno',
             'german':  'ein',
             'french':  'un'}}
D1 = D.copy()      # D1 is shallow copy with keys having refs to original values (sub-dict)
D['one'].clear()   # clear just values corresponding the key 'one' i.e. sub-dictionary
D, D1              # ({'one': {}}, {'one': {}})

dict.get()

xs = {'one': 'uno'}
# xs['two']                          # KeyError: 'two'

# Try to avoid exceptions:

def get_word(d, word, default=None):
    if word in d:
        return d[word]
    return default

get_word(xs, 'one', 'unknown')             # 'uno'
get_word(xs, 'two', 'unknown')             # 'unknown'

# BUT this usage could not be considered ideomatic!

# Let's try to get length of the string which is belongs to dict:

english_word = 'two'

# spanish_word = xs[english_word]          # Key Error ('two')
# spanish_word_length = len(spanish_word)

# spanish_word = xs.get(english_word)
# spanish_word_length = len(spanish_word)  # Type Error ('NoneType') -> get() does not solve the problem

# Use get() ONLY if we can provide a SENSIBLE DEFAULT:

spanish_word = xs.get(english_word, '')    #
spanish_word_length = len(spanish_word)
print(spanish_word_length)                 # 0

# BUT we can not blame .get() to avoid exceptions.
# This only delays this error and don't allow to father track it down.
# We SHOULD use the square bracket syntax and try...except block.

dict.update()

D = {1:1, 2:4, 3:9}
D1 = {1:0, 4:16}       # another mapping
D1.update(D)           # override values of the existing keys
D1

Output:

{1: 1, 4: 16, 2: 4, 3: 9}

dict.pop(), dict.popitem()

Retrieve and remove
xs = {'one': 'uno', 'two': 'dos'}
print(xs.pop('one'))     # uno
xs                       # {'two': 'dos'}

xs.pop('one')            # KeyError ('one')

xs.pop('one', 'default') # 'default'

xs.popitem()            # retrieve next item (key:value pair) in dict and remove it -  ('two', 'dos')
                        # items are not sorted => popitem() can return any item in dict

dict.keys(), dict.values(), dict.items()

d = {'uno': 'one', 'dos': 'two', 'tres': 'three'}

for key in d:
    print(key, end=',')            # uno,dos,tres,

for key in d.keys():
    print(key, end=',')            # uno,dos,tres,

for value in d.values():
    print (value, end=',')      # one,two,three,

for item in d.items():
    print(item, end=',')        # ('uno', 'one'),('dos', 'two'),('tres', 'three'),

# Note: The relative sorting of dict.keys(), dict.values(), dict.items() is garanteed.

# iterkeys, itervalues, iteritems in Python 2 - returns iterators of k,v,i of a dict
# viewkeys, viewvalues, viewitems in Python 2

d = {'uno': 'one', 'dos': 'two', 'tres': 'three'}
keys = d.keys()                  # symantec in Python 3 has changed. In Python 2 .keys() returns materialized list -
                                 # duplicates of dict keys in memory - inefficient.
keys                             # dict_keys(['uno', 'dos', 'tres']) - special datatype, view on a keys of a dict

# In Python 3, if we mutate a dict, the keys will be mutated also

d['cinco'] = 'five'
d                                # {'uno': 'one', 'dos': 'two', 'tres': 'three', 'cinco': 'five'}
keys                             # dict_keys(['uno', 'dos', 'tres', 'cinco']) - include new key

# Do not manipulate dict keys while we iterating over it.
Example:

for key in d:                    # RuntimeError: dictionary changed size during iteration
    if key == 'uno':
        del d[key]

# Create a set() for such a keys
deleted_keys = set()
for key in d:                    # RuntimeError: dictionary changed size during iteration
    if key == 'uno':
        deleted_keys.add(key)
# and delete keys
for key in deleted_keys:
    del d[key]

print(d)                         # {'dos': 'two', 'tres': 'three', 'cinco': 'five'}

dict.setdefault()

Manipulate the entries of a dict if not sure where the entries are exists or not.
student_courses = {'vinnie':    {'calculus', 'diff eq'},
                   'arnold':    {'calculus', 'linear algebra'},
                   'juan luis': {'real analysis'}}

student_courses['vinnie'].add('linear algebra')
print(student_courses)

# {'vinnie': {'calculus', 'diff eq', 'linear algebra'},  # OK
# 'arnold': {'calculus', 'linear algebra'},
#  'juan luis': {'real analysis'}}

student_courses['freddie'].add('linear algebra')         # KeyError: 'freddie'

if 'freddie' not in student_courses:
    student_courses['freddie'] = set()
student_courses['freddie'].add('linear algebra')         # OK

# BUT more simple method to initialize the key is to use default value:

student_courses.setdefault('freddie', set()).add('linear algebra')
student_courses.setdefault('rosalie', set()).add('calculus')
student_courses.setdefault('rosalie', set()).add('linear algebra')
print(student_courses)

# {'vinnie': {'calculus', 'diff eq', 'linear algebra'},
#  'arnold': {'calculus', 'linear algebra'},
# 'juan luis': {'real analysis'},
#  'freddie': {'linear algebra'},
#  'rosalie': {'calculus', 'linear algebra'}}

dict.__missing__

class lowercase_dict(dict):
    def __missing__(self, key):
        return self[key.lower()]

d = lowercase_dict({'uno': 'one', 'dos': 'two'})

# after that:

d['uNo']                              # 'one'
same as:
world = 'uNo'
d.get(world , d.get(world.lower()))   # 'one'

dict-comprehensions

{x: x**2 for x in range(10) if x%2 == 0}

Example - Substitution cipher

from string import ascii_lowercase
# ascii_lowercase

from random import shuffle
# shuffle(ascii_lowercase)           # TypeError: 'str' object does not support item assignment

alphabet = list(ascii_lowercase)
shuffle(alphabet)

codebook = {x: alphabet.pop() for x in ascii_lowercase}
print(codebook, end=';')
# {'a': 'g', 'b': 'v', 'c': 'q', 'd': 'p', 'e': 't', 'f': 'm', 'g': 'b', 'h': 'w', 'i': 'c', 'j': 'e', \
# 'k': 's', 'l': 'r', 'm': 'y', 'n': 'u', 'o': 'd', 'p': 'z', 'q': 'j', 'r': 'o', 's': 'a', 't': 'l', \
# 'u': 'f', 'v': 'h', 'w': 'x', 'x': 'k', 'y': 'i', 'z': 'n'};

message = 'python is great'
encoded = ''.join(codebook.get(char, '') for char in message)
# encoded                                                              # 'qcamwnyldgfua'
reverse_codebook = {v: k for k, v in codebook.items()}                 # values must be unique
print (''.join(reverse_codebook.get(m,'') for m in 'qcamwnyldgfua'))   # pythonisgreat